import 'dart:ui';

import 'package:flutter/material.dart' as ui;
import 'package:kq_flutter_pad_widgets/widgets/chart/pad_chart/base/pad_base_chart.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/base/axis.dart';
import 'package:kq_flutter_core_widget/utils/ex/string_ex.dart';

const String kEllipsis = '\u2026';

/// 轴线图基类
///
/// [D] 数据类型
/// Created by wanggaowan on 2023/9/15 08:37
abstract class PadBaseAxisChartDelegate<D> extends PadBaseChartDelegate<D> {
  PadBaseAxisChartDelegate(
      {super.animDuration,
      super.data,
      super.gestureHandler,
      super.emptyWidgetBuilder,
      super.isDataEmpty,
      super.canScroll,
      super.scrollWidth,
      required this.dataRender,
      required this.xAxis,
      required this.yAxis,
      required this.xAxisRender,
      required this.yAxisRender});

  /// x轴
  final BaseXAxis xAxis;

  /// y轴
  final BaseYAxis yAxis;

  /// Y轴绘制器
  final PadBaseYAxisRenderMixin yAxisRender;

  /// X轴绘制器
  final PadBaseXAxisRenderMixin xAxisRender;

  /// X轴与Y轴中间内容数据绘制器
  final PadBaseDataRenderMixin dataRender;

  @override
  void onDraw(ui.Canvas canvas, double animProgress, bool isDrawX, bool isDrawY,
      bool canScroll) {
    // 画布的开始和结束坐标
    double start = 0;
    double top = 0;
    double bottom = size.height;
    double end = size.width;
    // y轴绘制文本的最大宽度
    var yMaxTextWidth = getYAxisTextMaxWidth(yAxis);
    if (isDrawX && isDrawY && !canScroll) {
      // x轴轴线左边起始位置
      var xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis;
      initAxisLabel(yAxis);
      initAxisLabel(xAxis);

      // 网格宽度，x轴文本绘制的最大宽度
      var gridWidth = getGridWidth(
        xAxis.labelCount,
        end -
            xAxisStart -
            yAxis.lineWidth -
            xAxis.startPadding -
            xAxis.endPadding,
      );

      var xHeight = xAxisRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
            xAxisStart,
            top,
            end,
            bottom,
          ),
          gridWidth);

      // x轴轴线以下部分绘制内容高度结束位置
      var xAxisBottom = bottom - xHeight;
      //  x轴轴线y轴坐标
      var xAxisTop = xAxisBottom - xAxis.lineWidth;
      // 网格高度，y轴文本绘制的最大高度，
      var gridHeight = getGridHeight(yAxis.labelCount,
          xAxisTop - top - yAxis.startPadding - yAxis.endPadding);

      yAxisRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
              xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop),
          gridHeight,
          yMaxTextWidth,
          end - xAxisStart);

      dataRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
              xAxisStart + xAxis.startPadding + yAxis.lineWidth,
              top + yAxis.endPadding,
              end - xAxis.endPadding,
              xAxisTop - yAxis.startPadding),
          gridWidth,
          gridHeight,
          animProgress);
    } else if (isDrawX && canScroll) {
      // x轴轴线左边起始位置
      var xAxisStart = start;
      initAxisLabel(xAxis);

      // 网格宽度，x轴文本绘制的最大宽度
      var gridWidth = getGridWidth(
        xAxis.labelCount,
        end -
            xAxisStart -
            yAxis.lineWidth -
            xAxis.startPadding -
            xAxis.endPadding,
      );

      var xHeight = xAxisRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
            xAxisStart,
            top,
            end,
            bottom,
          ),
          gridWidth);

      // x轴轴线以下部分绘制内容高度结束位置
      var xAxisBottom = bottom - xHeight;
      //  x轴轴线y轴坐标
      var xAxisTop = xAxisBottom - xAxis.lineWidth;
      // 网格高度，y轴文本绘制的最大高度，
      var gridHeight = getGridHeight(yAxis.labelCount,
          xAxisTop - top - yAxis.startPadding - yAxis.endPadding);

      dataRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
              xAxisStart +
                  xAxis.startPadding +
                  (canScroll ? 0 : yAxis.lineWidth),
              top + yAxis.endPadding,
              end - xAxis.endPadding,
              xAxisTop - yAxis.startPadding),
          gridWidth,
          gridHeight,
          animProgress);
    } else if (isDrawY && canScroll) {
      // x轴轴线左边起始位置
      var xAxisStart = start + yMaxTextWidth + yAxis.labelOffsetAxis;
      initAxisLabel(yAxis);

      // x轴轴线以下部分绘制内容高度结束位置
      var xAxisBottom = bottom - xAxisRender.getXHeight();
      //  x轴轴线y轴坐标
      var xAxisTop = xAxisBottom - xAxis.lineWidth;
      // 网格高度，y轴文本绘制的最大高度，
      var gridHeight = getGridHeight(yAxis.labelCount,
          xAxisTop - top - yAxis.startPadding - yAxis.endPadding);

      yAxisRender.onDraw(
          this,
          canvas,
          ui.Rect.fromLTRB(
              xAxisStart, top, (xAxisStart + yAxis.lineWidth), xAxisTop),
          gridHeight,
          yMaxTextWidth,
          end - xAxisStart);
    }
  }

  /// 获取两根网格线之间的宽度
  ///
  /// [labelCount]为轴上标签数量，[xAxisLength]为轴的整体宽度
  double getGridWidth(int labelCount, double xAxisLength) {
    labelCount = labelCount - 1;
    if (labelCount < 1) {
      labelCount = 1;
    }

    return xAxisLength / labelCount;
  }

  /// 获取两根网格线之间的高度
  ///
  /// [labelCount]为轴上标签数量，[yAxisLength]为轴的整体高度
  double getGridHeight(int labelCount, double yAxisLength) {
    labelCount = labelCount - 1;
    if (labelCount < 1) {
      labelCount = 1;
    }

    return yAxisLength / labelCount;
  }

  /// 初始化轴上要绘制标签列表
  void initAxisLabel(Axis axis) {
    List<Label> labelList = [];
    for (int i = 0; i < axis.labelCount; i++) {
      var label = axis.axisValueFormatter.call(i);
      labelList.add(label);
    }

    axis.$labelList = labelList;
  }

  /// 获取Y轴上文本绘制的最大宽度
  double getYAxisTextMaxWidth(BaseYAxis axis) {
    double? labelWidth = axis.labelWidth;
    double? labelMaxWidth = axis.labelMaxWidth;

    if (labelWidth != null) {
      if (labelWidth < axis.labelMinWidth) {
        return axis.labelMinWidth;
      } else if (labelMaxWidth == null || labelWidth < labelMaxWidth) {
        return labelWidth;
      } else {
        return labelMaxWidth;
      }
    }

    var textPaint = ui.TextPainter(textDirection: TextDirection.ltr);
    var labelList = axis.labelList;
    double maxTextWidth = 0;
    if (axis.labelTextSize > 0 && labelList != null && labelList.isNotEmpty) {
      for (var value in labelList) {
        if (value.weight <= 0 || value.value.isNullOrEmpty) {
          continue;
        }

        textPaint.text = ui.TextSpan(
            text: value.value,
            style: ui.TextStyle(fontSize: axis.labelTextSize));
        textPaint.layout();
        var width = textPaint.width;
        if (width > maxTextWidth) {
          maxTextWidth = width;
        }
      }
    }

    if (maxTextWidth < axis.labelMinWidth) {
      return axis.labelMinWidth;
    } else if (labelMaxWidth == null || maxTextWidth < labelMaxWidth) {
      return maxTextWidth;
    } else {
      return labelMaxWidth;
    }
  }
}

/// 图表数据绘制器
///
/// Created by wanggaowan on 2023/9/15 08:30
mixin PadBaseDataRenderMixin<C extends PadBaseAxisChartDelegate> {
  /// 绘制图表数据，参数中的坐标为实际可绘制的起始和结束坐标，已自动处理了[Axis]中设置的偏移值。
  /// 此时[AxisEntity.drawRect]未就绪
  ///
  /// [rect] 可绘制区域范围
  /// [gridWidth] 网格宽度
  /// [gridHeight] 网格高度
  /// [animProgress] 动画进度
  void onDraw(C chart, ui.Canvas canvas, ui.Rect rect, double gridWidth,
      double gridHeight, double animProgress);
}

/// X轴绘制器
///
/// Created by wanggaowan on 2023/9/15 08:30
mixin PadBaseXAxisRenderMixin<C extends PadBaseAxisChartDelegate> {
  /// 绘制X轴内容，绘制完成返回X轴占用内容的高度(不包含轴线的高度)。[rect.bottom]-返回的高度，则为表格数据可绘制的底部坐标
  ///
  /// [rect] 轴线绘制区域范围。[rect.left],[rect.right]为实际绘制的起始和结束坐标。[rect.top]、[rect.bottom]为整个表格的顶部和底部。
  /// [gridWidth] 每个网格的宽度
  double onDraw(C chart, ui.Canvas canvas, ui.Rect rect, double gridWidth);

  double getXHeight();
}

/// Y轴绘制器
///
/// Created by wanggaowan on 2023/9/15 08:30
mixin PadBaseYAxisRenderMixin<C extends PadBaseAxisChartDelegate> {
  /// 绘制Y轴内容,end - start = y轴轴线的粗细，end - start + labelMaxWidth + [Axis.labelOffsetAxis] = Y轴占据的总宽度
  ///
  /// [rect] 轴线绘制区域范围
  /// [labelMaxWidth] 标签权重为1时每个标签最大可绘制的宽度
  /// [gridHeight] 每个标签权重为1时最大可绘制的高度
  /// [xAxisRange] x轴轴线的长度
  void onDraw(C chart, ui.Canvas canvas, ui.Rect rect, double gridHeight,
      double labelMaxWidth, double xAxisRange);
}

/// 轴线图手势处理基类
abstract class PadBaseAxisChartGestureHandler<
    C extends PadBaseAxisChartDelegate,
    D> with PadBaseChartGestureHandlerMixin<C> {
  /// 构建基础的手势处理器
  ///
  /// [touchData]为当前选中数据，主动赋值相当于模拟用户点击
  PadBaseAxisChartGestureHandler(
      {this.tapEnable = true,
      this.dragEnable = true,
      D? touchData,
      this.onTap}) {
    _touchData = touchData;
  }

  @override
  final bool tapEnable;
  @override
  final bool dragEnable;

  /// 点击监听，回调参数为空说明点击了非特定数据区域，不为空则为某个数据的绘制区域
  final void Function(D? data)? onTap;

  D? get touchData => _touchData;
  D? _touchData;

  @ui.protected
  C? get chart => _chart;
  C? _chart;

  @override
  void attachChart(C delegate) {
    _chart = delegate;
  }

  @override
  void dispose() {
    _chart = null;
  }

  @override
  void onTapUp(ui.DragDownDetails details) {
    if (_chart == null || !tapEnable) {
      return;
    }

    findTouchInRange(details.localPosition, false);
  }

  @override
  void onTapDown(ui.DragDownDetails details) {}

  @override
  void onDragEnd(ui.DragEndDetails details) {}

  @override
  void onDragStart(ui.DragStartDetails details) {
    _onScroll(details.localPosition);
  }

  @override
  void onDragUpdate(ui.DragUpdateDetails details) {
    if (details.delta.dx.abs() < details.delta.dy.abs()) {
      return;
    }
    _onScroll(details.localPosition);
  }

  void _onScroll(ui.Offset offset) {
    if (_chart == null || !dragEnable) {
      return;
    }

    findTouchInRange(offset, true);
  }

  @ui.protected
  void findTouchInRange(ui.Offset offset, bool isMove) {
    var data = getTouchData(offset, isMove);
    if (data == null) {
      if (!isMove && _touchData != null) {
        _touchData = null;
        _chart?.update();
        onTap?.call(_touchData);
      }
      return;
    }

    _touchData = data;
    _chart?.update();
    onTap?.call(_touchData);
  }

  /// 返回触摸点坐标[ui.Offset]对应位置的高亮数据,如果未找到返回null
  D? getTouchData(ui.Offset offset, bool isMove);

  void update(PadBaseChartGestureHandlerMixin? other) {
    if (other == null || other is! PadBaseAxisChartGestureHandler) {
      return;
    }

    if (_touchData == null) {
      var old = other._touchData;
      if (old != null && old.runtimeType == D.runtimeType) {
        _touchData = old;
      }
    }
  }
}
